
/* GCSx
** FRAME.H
**
** Frame windows
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#ifndef __GCSx_FRAME_H_
#define __GCSx_FRAME_H_

class FrameWindow : public Window {
public:
    enum FrameType {
        // Frame types
        FRAMETYPE_BEVEL_TEXT, // Bevel with textbox color bk
        FRAMETYPE_BEVEL_BK,   // Bevel with background color
        FRAMETYPE_DIALOG,
        FRAMETYPE_NOCHANGE,
    };

    enum TitlebarType {
        // Titlebar types
        TITLEBAR_OFF,
        TITLEBAR_NORMAL,
        TITLEBAR_TOOL,
        TITLEBAR_NOCHANGE,
    };

    enum ResizableType {
        // Resizing behaviors
        RESIZING_OFF = 0,
        RESIZING_NORMAL,
        RESIZING_SNAP, // Mostly for panels/toolbars- can't resize to a size the child window won't accept
        RESIZING_NOCHANGE,
    };

    enum {
        CLIENT_PRIMARY = 0,
        // These are all purposefully chosen sequences/values
        CLIENT_RIGHT = 0, // (default position)
        CLIENT_LEFT = 1,
        CLIENT_TOPBOTTOM = 2, // &this means top or bottom
        CLIENT_TOP = 2,
        CLIENT_BOTTOM = 3,
        CLIENT_GETSIDE = 3, // &this to get which side we're on
        // Free-floating
        CLIENT_FLOAT = 4,
        // OR with previous- Docked onto the desktop instead of window
        CLIENT_DOCKED = 8,
        // OR with previous- HIDDEN due to resizing of window
        CLIENT_HIDDEN = 16,
        // OR with previous- CLOSED means manually closed (toggle)
        CLIENT_CLOSED = 32,
        // &this means it's not visible or not docked onto our window
        CLIENT_NOTMANAGED = 60,
    };

protected:
    enum DragType {
        // Drag types
        FRAME_DRAG_NONE,
        FRAME_DRAG_TITLE,
        FRAME_DRAG_BUTTONS,
        FRAME_DRAG_BORDER,
        // Includes tool panels, etc.- index in subtype
        FRAME_DRAG_CLIENT, 
        FRAME_DRAG_SCROLLBAR_HORIZ,
        FRAME_DRAG_SCROLLBAR_VERT,
    };

    enum DragSubType {
        // Drag subtypes, can be OR'd (integers 1+ also used for titlebar buttons)
        FRAME_RESIZE_NONE = 0,
        FRAME_RESIZE_BOTTOM = 1,
        FRAME_RESIZE_TOP = 2,
        FRAME_RESIZE_LEFT = 4,
        FRAME_RESIZE_RIGHT = 8,
        // Drag subtypes for scrollbar
        FRAME_SCROLL_NONE = 0,
        FRAME_SCROLL_LESS, // Left/Up
        FRAME_SCROLL_PAGELESS, // Page Left/Up
        FRAME_SCROLL_MORE, // Right/Down
        FRAME_SCROLL_PAGEMORE, // Page Right/Down
        FRAME_SCROLL_TAB,
    };

    enum StateType {
        // State types
        FRAME_STATE_NORMAL = 0,
        FRAME_STATE_MINIMIZED = 1,
        FRAME_STATE_MAXIMIZED = 2,
    };
    
    enum {
        // Dirty reasons, can be OR'd
        FRAME_DIRTY_NONE = 0,
        FRAME_DIRTY_TITLE = 1, // Includes buttons
        FRAME_DIRTY_SCROLLVERT = 2,
        FRAME_DIRTY_SCROLLHORIZ = 4,
        FRAME_DIRTY_ALL = 255, // If resized or tool size or area/scroll existence changes
    };
    
    ResizableType resizable;
    int deleteMe;
    FrameType frameType;
    int outerBorder;
    class FrameWindow* framePanel; // Are we a panel on another frame window? (in parentnotify) resizing/moving handled differently
    TitlebarType titlebarType;
    std::string titlebar;
    StateType state;
    int whyDirty;
    
    // Used for cascading windows
    static int cascadeStep;
    
    int titlebarX;
    int titlebarY;
    int titlebarWidth;
    int titlebarHeight;
    int titlebarButtonX;
    int titlebarButtonY;
    int titlebarButtonSize;
    int titlebarButtonXPad;
    int titlebarButtonTotalWidth;
    char titlebarButtons[5];

    int scrollbarButtonSize;
    int scrollbarButtonXPad;

    // (width and position of our client areas- not necessarily width/position
    //  of client window internally, which can be larger/smaller and scroll)
    // X/Y of -1 means not calculated yet; Width/Height of -1 means not
    // calculated and first time will use requested size by client area; after
    // that, the previous width/height will be used for all recalculations
    // Shown is the actual width/height of the area; other value is what we'd
    // like/request if possible; for main client area, "shown" is same as reg.

    // 0 = client 1+ = tool panels/toolbars (optional)
    struct ClientArea {
        Window* window;
        int x;
        int y;
        int width;
        int height;
        int widthShown;
        int heightShown;
        int position;
    };
    std::vector<ClientArea> clientArea;
    
    // Inner sizing of main client area (not including scrollbars, basically)
    int clientWidthInner;
    int clientHeightInner;
    
    // Amount of space reserved on each side for non-primary client areas
    int reserve[4];

    // Smallest size for a non-primary client area (height for left/right, width for top/bottom)
    static int minimumPanelSize;
    
    // Width and height of main client area
    // Reupdated whenever we resize()
    int clientActualWidth;
    int clientActualHeight;
    
    // Destination position for a floating panel being dragged (show highlighted)
    int floatTarget;
    int floatTargetX;
    int floatTargetY;
    int floatTargetWidth;
    int floatTargetHeight;
    
    // Scroll offset (usually zero or negative; positive offsets used to center in view area)
    int scrollX;
    int scrollY;
    int autoCenterScroll;
    
    // Scroll sizes- how much to scroll
    int vScrollLine;
    int vScrollPage;
    int hScrollLine;
    int hScrollPage;
    
    // Position to draw scrollbars; set to 0 if no scrollbars
    // Size of actual drawn scrollbars
    int vScrollbarX;
    int vScrollbarY;
    int vScrollbarWidth;
    int vScrollbarHeight;
    int vScrollbarTab; // Offset within scroll area
    int vScrollbarTabSize; // 0 = tab not shown
    int hScrollbarX;
    int hScrollbarY;
    int hScrollbarWidth;
    int hScrollbarHeight;
    int hScrollbarTab; // Offset within scroll area
    int hScrollbarTabSize; // 0 = tab not shown
    
    // Used for autorepeating scrollbar actions
    Uint32 scrollRepeatTicks;
    Uint32 scrollRepeatDelay;
    
    int restoreX;
    int restoreY;
    int restoreWidth;
    int restoreHeight;
    
    int haveFocus;
    int subFocus; // 0 client 1+ tool areas
    
    // Internal- for dragging titlebar, borders, etc.
    // hoverMode used just for hovering
    DragType dragMode;
    DragType hoverMode;
    DragSubType dragItem;
    DragSubType hoverItem;
    int dragX;
    int dragY;
    int dragItemOverflowX;
    int dragItemOverflowY;
    
    // Change states
    void maximize();
    void minimize();
    int isMinimized();
    void restore();
    void nextWindow();
    void prevWindow();
    
    // Takes an x/y/width/height and confines them to desktop/screen
    void confineSize(int& sX, int& sY, int& sWidth, int& sHeight) const;
    
    // Scrolls to a given -X, -Y
    // Automatically clips scroll to best possible
    // Can specify x and/or y for scrollbar tabs to force if needed
    void scrollTo(int sX, int sY, int tabX = -1, int tabY = -1);

    // Which area is a given coords in? titlebar, buttons, border, etc.
    // Returns a drag constant, and if applicable, a drag item (resize constant or button number)
    void whereCoords(int x, int y, DragType* dragType, DragSubType* dragItem) const;
    
    // Handles movement of mouse hover- called on mouse motion and after mouse btn release
    void doMouseHover(int x, int y, int hasFocus, SDL_Event& clientEvent);
    
    // Recalculates reserve*, clientX, clientY, clientWidth, clientHeight
    // Does not recalculate client*Innder or redraw *anything*
    void reorganizeReserves();

    enum {
        // Sizing constants
        FRAME_OUTERBORDER = 3,
        FRAME_INNERBORDER = 2,
        FRAME_BEVELTHICKNESS = 2,
        FRAME_TITLELEFTPAD = 5,
        FRAME_TITLETOPPAD = 3,
        FRAME_TITLEBOTTOMPAD = 2,
        FRAME_TOOLTITLELEFTPAD = 3,
        FRAME_TOOLTITLETOPPAD = 2,
        FRAME_TOOLTITLEBOTTOMPAD = 1,
        // Gutter between various client/tool areas
        FRAME_CLIENTPAD = 1,
        
        // Minimum size for scrollbar tab
        FRAME_SCROLLTABMIN = 10,

        // Distance from edge to allow corner-based resizing
        RESIZE_CORNER_SENSITIVITY = 13,

        // MS to wait before repeating scrollbar action initially
        DELAY_SCROLLBAR_REPEATSTART = 500,
        
        // MS to wait between additional repeats of scrollbar action
        DELAY_SCROLLBAR_REPEATAGAIN = 75,
        
        // Minimum "default" size for a frame window
        DEFAULT_MIN_WIDTH = 150,
        DEFAULT_MIN_HEIGHT = 100,
    };

    static const char* wtButtonAll;
    static const char* wtButtonNoResize;
    static const std::string wtButtonClose;
    static const std::string wtButtonUp;
    static const std::string wtButtonDown;
    static const std::string wtButtonLeft;
    static const std::string wtButtonRight;
    
public:
    // Width and height default to fitting to the client area
    // These can easily be changed when you run show() to display the window
    // Scrollbars are supplied as needed and client gets resizes to update view size
    // Client area pointer will be deleted by us if it wantsToBeDeleted().
    // We don't take ownership of pointer if constructor fails.
    FrameWindow(const std::string& title, ResizableType isResizable, FrameType type, Window* client, TitlebarType hasTitlebarType = TITLEBAR_NORMAL, int hasOuterBorder = 1, class FrameWindow* isFramePanel = NULL);
    ~FrameWindow();

    Window* getClient() { return clientArea[0].window; }
    
    // Removes all clients from us so we can be deleted without deleting
    // client windows; typically called from a failed constructor; doesn't modify
    // appearance, etc.
    void dropClients();
    
    // Adds a tool panel
    // Tool panels don't get attemptClose or undoNotify calls
    void addToolPanel(FrameWindow* toolPanel, int position, std::vector<ClientArea>::iterator* insertBefore = NULL);
    void removeToolPanel(Window* toolPanel);
    // Returns 0 if not an existing tool panel, otherwise returns an index
    int findToolPanel(Window* toolPanel);
    // index of 0 to focus on client area; only works if panel is docked
    void focusToolPanel(int index);
    // Toggles visible and not visible, for now; only works if panel is docked
    void toggleToolPanel(int index, int giveFocusIfOpen = 1);
    // Handles dragging of a docked  panel
    void dragToolPanel(int index, int xMotion, int yMotion, int& dragItemOverflowX, int& dragItemOverflowY);//@TODO:throw
    // Handles dragging of a floating panel; 0 = dragging, 1 = dropped
    void dragFloatPanel(int index, int drop);//@TODO:throw
    
#ifndef NDEBUG
    const char* debugDump() const;
#endif

    // Call when desktop area resizes to ensure we're sized/moved properly
    void redoSize();

    // Client should call this whenever it resizes
    // This will update scrollbars
    // Default Window::resize() will call this if appropriate
    void updateClientSize();
    
    // Client can request a minimum given view size
    // This won't ever reduce window size
    void requestViewSize(int requestWidth, int requestHeight);
    
    // Client may call this to set scrolling amounts; if not called,
    // a default size is used (page amounts are automatically
    // determined as the full width/height minus one "line")
    void setScroll(int vLine, int hLine);
    // Automatically center inner frame, if smaller than available size
    void setAutoCenter(int newAutoCenter);

    // Scrolls to show as much of the given rectangle as possible
    // Automatically clips scroll to best possible
    void scrollToView(int sX, int sY, int sWidth, int sHeight);
    // Scrolls to offset from current scroll
    void scrollBy(int sX, int sY);
    
    void setTitle(const std::string& newTitle);
    // use _NOCHANGE constants or -1 to initiate no change for an item
    // cannot undo framepanel
    void setFeatures(ResizableType isResizable, FrameType type = FRAMETYPE_NOCHANGE, TitlebarType hasTitlebarType = TITLEBAR_NOCHANGE, int hasOuterBorder = -1, class FrameWindow* isFramePanel = NULL, int skipResizing = 0);
    void resize(int newWidth, int newHeight, int newViewWidth = -1, int newViewHeight = -1, int fromParent = 0);
    void display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset);
    void resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp);
    void childMoved(int fromX, int fromY, int toX, int toY, Window* child);
    void childResized(int fromW, int fromH, int toW, int toH, Window* child);
    void childDeleted(Window* child);
    const char* tooltip(int xPos, int yPos) const;
    void undoNotify(int undoType, int undoItemId, int undoItemSubId, int uX, int uY, int uW, int uH);
    
    int event(int hasFocus, const SDL_Event* event);
    /* Not currently used or supported
    int usesAlpha();
    */
    WindowType windowType() const;
    WindowSort windowSort() const;
    CommandSupport supportsCommand(int code) const;
    
    // Propogates to all other children via siblingModified
    void childModified(Window* child);
    void siblingModified(Window* sibling);
    
    // This is whether the frame window wants to be deleted when it recieves SDL_CLOSE,
    // and defaults to YES
    int wantsToBeDeleted() const;
    
    // Lets us set whether we want the frame window to delete itself
    void setWantsToBeDeleted(int fDeleteMe);

    int attemptClose();

    // Adds window to gui onscreen with focus/removes
    // Once shown, it can receive a close message and delete itself, unless show() fails
    // xPos/yPos can come from these constants, or be >= 0
    enum {
        SHOW_CASCADE = -1, // Default cascading size / pos
        SHOW_CURRENT = -2, // Current size / pos (which, if just created, fits the client/toolpanels)
        SHOW_CURRMIN = -3, // Current size / pos, but with a minimum default size
    };
    
    // Show/hide on desktop
    // Size is for entire frame window, not client area
    void show(int xPos = SHOW_CASCADE, int yPos = SHOW_CASCADE, int width = SHOW_CASCADE, int height = SHOW_CASCADE);
    void hide(); // Manually hiding won't delete the frame window, ever
    
    class FrameWindow* getFramePanel();
};

#endif

